/*
 * EjudgeLogonService.h
 *
 *  Created on: Dec 24, 2011
 *      Author: andy
 */

#ifndef EJUDGELOGONSERVICE_H_
#define EJUDGELOGONSERVICE_H_

#include <string>
#include <vector>
#include <exception>
#include <boost/scoped_ptr.hpp>
#include "boost/thread.hpp"
#include "boost/date_time.hpp"
#include "boost/multi_index_container.hpp"
#include "boost/multi_index/member.hpp"
#include "boost/multi_index/ordered_index.hpp"
#include "boost/multi_index/hashed_index.hpp"
#include "boost/multi_index/identity.hpp"

struct Settings
{
    std::string login_;
    std::string password_;
    std::string ejudge_socket_path_;
    std::string out_socket_path_;
    std::string log_path_;
    boost::posix_time::time_duration session_duration_;
    int max_contests_count_;
    Settings(const std::string& filename)
    {
        load(filename);
    }
    void load(const std::string& filename);
};

class EjudgeLogonServiceException : std::exception {
public:
	EjudgeLogonServiceException(std::string message) : message_(message) {}
	std::string message_;
	virtual ~EjudgeLogonServiceException() throw() {}
};

struct LogonEntrySocket {
    enum types { client, master };
    char login_[20];
    char password_[20];
    char ip_[20];
    unsigned int contest_id_;
    unsigned int user_id_;
    unsigned int type_;
};

struct LogonMapEntry {
    LogonMapEntry(int user_id, std::string &sid, boost::posix_time::ptime logon_time) :
        user_id_(user_id), sid_(sid), time_(logon_time) {}
    
    LogonMapEntry() {}
    
    int user_id_;
    std::string sid_;
    boost::posix_time::ptime time_;
};

struct LogonEntry {
	LogonEntry(int user_id, const std::string& login,const std::string& password,const std::string& ip, int contest_id = 0) :
	    user_id_(user_id), login_(login), password_(password), ip_(ip), contest_id_(contest_id) {}

    LogonEntry(const LogonEntrySocket& entry) :
        user_id_(entry.user_id_),
        login_(entry.login_),
        password_(entry.password_),
        ip_(entry.ip_),
        contest_id_(entry.contest_id_)
        {}


	LogonEntrySocket getSocketEntry()
	{
	    LogonEntrySocket res;
	    strncpy(res.login_, login_.c_str(), sizeof(res.login_));
        strncpy(res.password_, password_.c_str(), sizeof(res.password_));
        strncpy(res.ip_, ip_.c_str(), sizeof(res.ip_));
	    res.contest_id_ = contest_id_;
	    res.user_id_ = user_id_;
	    return res;
	}

	unsigned int user_id_;
	std::string login_;
	std::string password_;
	std::string ip_;
	unsigned int contest_id_;
};

struct LogonResultSocket
{
    char name_[100];
    unsigned int user_id_;
    char sid_[20];
};

struct LogonResult
{
    LogonResult() : name_("Empty result"), user_id_(0), sid_("") {};
    LogonResult(LogonResultSocket &socketRes) :
        name_(socketRes.name_),
        user_id_(socketRes.user_id_),
        sid_(socketRes.sid_)
    {};

    LogonResult(const std::string &name, int user_id, const std::string &sid) :
        name_(name),
        user_id_(user_id),
        sid_(sid)
    {};

    LogonResultSocket getSocketEntry()
    {
        LogonResultSocket res;
        strncpy(res.name_, name_.c_str(), sizeof(res.name_));
        strncpy(res.sid_, sid_.c_str(), sizeof(res.sid_));
        res.user_id_ = user_id_;
        return res;
    }

    std::string name_;
    int user_id_;
    std::string sid_;
};

using namespace boost;
using namespace boost::multi_index;


typedef multi_index_container<
        LogonMapEntry,
        indexed_by< hashed_unique< member<LogonMapEntry, int, &LogonMapEntry::user_id_> >,
                    ordered_non_unique< member< LogonMapEntry, posix_time::ptime, &LogonMapEntry::time_> >
        > > logon_container;


class EjudgeClnt;

class EjudgeLogonService {
public:
	EjudgeLogonService(Settings &settings);
	LogonResult getLogon(const LogonEntry& entry);
    LogonResult getMasterLogon(const LogonEntry& entry);
	virtual ~EjudgeLogonService();
private:
	void cleanUp(logon_container &c);
	std::vector<logon_container> contest_logons;
    std::vector<LogonMapEntry> priv_contest_logons;
    boost::posix_time::time_duration session_duration_;
	boost::scoped_ptr<EjudgeClnt> clnt;
	boost::mutex m;
};



#endif /* EJUDGELOGONSERVICE_H_ */
